#include <stdlib.h>
#include <assert.h>
#include "action.h"
#include "actor.h"
#include "game.h"
#include "main.h"
#include "items.h"
#include "util/log.h"

static void action_init (Action* action, ActionType type, int actor,
                         int start_time, int cooldown)
{
	assert((actor >= 0) && (actor < the_game.map.actor.count));
	
	action->type = type;
	action->actor = actor;
	action->start_time = start_time;
	action->cooldown = cooldown;
	action->turn = NULL; // to all pointers in the union
}

Action action_alloc_turn (int actor, int start_time, Direction direction)
{
	assert((direction == DIR_DOWN) || (direction == DIR_UP) ||
	       (direction == DIR_LEFT) || (direction == DIR_RIGHT));
	
	Action action;
	action_init (&action, ACTION_TURN, actor, start_time, COOLDOWN_TURN);
	action.turn = malloc (sizeof(TurnAction));
	action.turn->direction = direction;
	return action;
}

Action action_alloc_move (int actor, int start_time)
{
	Action action;
	action_init (&action, ACTION_MOVE, actor, start_time, COOLDOWN_MOVE);
	return action;
}

Action action_alloc_interact (int actor, int start_time, int target)
{
	assert((actor >= 0) && (actor < the_game.map.actor.count));
	assert(map_get_actor (&the_game.map, actor)->type == ACTOR_CHARACTER); // no monsters
	assert((target >= 0) && (target < the_game.map.object.count));
	
	Action action;
	action_init (&action, ACTION_INTERACT, actor, start_time, COOLDOWN_INTERACT);
	action.interact = malloc (sizeof(InteractAction));
	action.interact->target = target;
	return action;
}

Action action_alloc_halt (int actor, int start_time)
{
	Action action;
	action_init (&action, ACTION_HALT, actor, start_time, COOLDOWN_HALT);
	return action;
}

Action action_alloc_closeattack (int actor, int start_time, int weapon)
{
	assert((weapon >= WEAPONS_CLOSE_BEGIN) && (weapon < WEAPONS_CLOSE_END));
	
	Action action;
	action_init (&action, ACTION_ATTACK_CLOSE, actor, start_time, COOLDOWN_ATTACK);
	action.cattack = malloc (sizeof(CloseAttackAction));
	action.cattack->weapon = weapon;
	return action;
}

Action action_alloc_rangeattack (int actor, int start_time, int weapon)
{
	assert((weapon >= WEAPONS_RANGE_BEGIN) && (weapon < WEAPONS_RANGE_END));
	
	Action action;
	action_init (&action, ACTION_ATTACK_RANGE, actor, start_time, COOLDOWN_ATTACK);
	action.rattack = malloc (sizeof(RangeAttackAction));
	action.rattack->weapon = weapon;
	return action;
}

Action action_alloc_traverse (int actor, int start_time, Coord target)
{
	assert((target.x >= 0) && (target.x < the_game.map.width));
	assert((target.y >= 0) && (target.y < the_game.map.height));
	assert((target.z >= 0) && (target.z < the_game.map.layers));
	
	Action action;
	action_init (&action, ACTION_TRAVERSE, actor, start_time, COOLDOWN_TRAVERSE);
	action.traverse = malloc (sizeof(TraverseAction));
	action.traverse->target = target;
	return action;
}

Action action_alloc_die (int actor, int start_time)
{
	Action action;
	action_init (&action, ACTION_DIE, actor, start_time, COOLDOWN_DIE);
	return action;
}

extern Action action_alloc_respawn (int actor, int start_time)
{
	Action action;
	action_init (&action, ACTION_RESPAWN, actor, start_time, COOLDOWN_RESPAWN);
	return action;
}

void action_free (Action* a)
{
	switch (a->type) {
	case ACTION_TURN:         free (a->turn);     a->turn     = NULL; break;
	case ACTION_INTERACT:     free (a->interact); a->interact = NULL; break;
	case ACTION_ATTACK_CLOSE: free (a->cattack);  a->cattack  = NULL; break;
	case ACTION_ATTACK_RANGE: free (a->rattack);  a->rattack  = NULL; break;
	case ACTION_TRAVERSE:     free (a->traverse); a->traverse = NULL; break;
	case ACTION_MOVE:
	case ACTION_HALT:
	case ACTION_DIE:
	case ACTION_RESPAWN:
	case ACTION_NONE: break; // nothing to do
	default: assert(!"cannot free unknown action");
	}
	a->cattack = NULL; 
}

bool action_is_equal (const Action* a1, const Action* a2)
{
	if (a1->type != a2->type) return false;
	if (a1->actor != a2->actor) return false;
	if (a1->start_time != a2->start_time) return false;
	
	return true;
}
